初学Sass过程中的笔记

在学习之前,我推荐一个可以在线编辑sass的编辑器,点击这里打开网站,按如图
cmd-markdown-logo所示绿色箭头的New Pen进去后,再点击如图
cmd-markdown-logocss那块的设置,进去设置如下
cmd-markdown-logo最后点击绿色按钮保存就好了,接下来就可以在线学习了。

正文开始:

1.Sass 文件后缀名
sass 有两种后缀名文件:一种后缀名为 sass,不使用大括号和分号;另一种就是我们这里使用的 sass 文件,这种和我们平时写的 css 文件格式差不多,使用大括号和分号。本教程中所说的所有 sass 文件都指后缀名为 scss 的文件。在此也建议使用后缀名为 scss 的文件,以避免 sass 后缀名的严格格式要求报错。

2. 使用变量
sass让人们受益的一个重要特性就是它为css引入了变量。你可以把反复使用的css属性值定义成变量,然后通过变量名来引用它们,而无需重复书写这一属性值。或者,对于仅使用过一次的属性值,你可以赋予其一个易懂的变量名,让人一眼就知道这个属性值的用途。
2-1. 变量声明
sass变量的声明和css属性的声明很像:

1
$yellow: #F90;

这意味着变量$yellow现在的值是#F90,也就是我自己定义的黄色。任何可以用作css属性值的赋值都可以用作sass的变量值,与CSS属性不同,变量可以在css规则块定义之外存在。当变量定义在css规则块内,那么该变量只能在此规则块内使用,简单来说就是这个变量是有作用域的。

1
2
3
4
5
6
7
8
9
10
11
12
13
$yellow: #F90;
h1 {
$width: 100px;
width: $width;
color: $yellow;
}
//编译后
h1 {
width: 100px;
color: #F90;
}

在这段代码中,$yellow变量定义在了规则块外边,所以在这个样式表中都可以像h1规则块那样引用它。
$width这个变量定义在了h1的{ }规则块内,所以它只能在h1规则块内使用。这意味着是你可以在样式表的其他地方定义和使用$width变量,不会对这里造成影响。
2-2. 变量引用
凡是css属性的标准值(比如说1rem或者red)可存在的地方,变量就可以使用。css生成时,变量会被它们的值所替代。之后,如果你需要一个不同的值,只需要改变这个变量的值,则所有引用此变量的地方生成的值都会随之改变。

1
2
3
4
5
6
7
8
9
10
$yellow: #F90;
.selected {
border: 1px solid $yellow;
}
//编译后
.selected {
border: 1px solid #F90;
}

当这段代码被编译输出css时,$yellow会被#F90这一颜色值所替代。

在声明变量时,变量值也可以引用其他变量。

1
2
3
4
5
6
7
8
9
10
11
$yellow: #F90;
$border: 1px solid $yellow;
.selected {
border: $border;
}
//编译后
.selected {
border: 1px solid #F90;
}

2-3. 变量名用中划线和下划线分隔都是可以的
sass并不想强迫任何人一定使用中划线或下划线,所以这两种用法相互兼容。用中划线声明的变量可以使用下划线的方式引用,反之亦然。

1
2
3
4
5
6
7
8
9
10
$link-color: blue;
a {
color: $link_color;
}
//编译后
a {
color: blue;
}

3. 嵌套CSS 规则
Sass中,你可以在规则块中嵌套规则块。sass在输出css时会帮你把这些嵌套规则处理好,避免你的重复书写。

1
2
3
4
5
6
7
8
9
10
11
12
#content {
article {
h1 { color: #333 }
p { margin-bottom: 1.4em }
}
aside { background-color: #EEE }
}
/* 编译后 */
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }

Sass会一步步把你的嵌套打开

1
2
3
4
5
6
7
8
9
10
11
12
#content {
article {
h1 { color: #333 }
p { margin-bottom: 1.4em }
}
#content aside { background-color: #EEE }
}
/* 编译后 */
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }

一个给定的规则块,既可以像普通的CSS那样包含属性,又可以嵌套其他规则块。当你同时要为一个容器元素及其子元素编写特定样式时,这种能力就非常有用了。

1
2
3
4
#content {
background-color: #f5f5f5;
aside { background-color: #eee }
}

容器元素的样式规则会被单独抽离出来,而嵌套元素的样式规则会像容器元素没有包含任何属性时那样被抽离出来。

1
2
#content { background-color: #f5f5f5 }
#content aside { background-color: #eee }

大多数情况下这种简单的嵌套都没问题,但是有些场景下不行,比如你想要在嵌套的选择器 里边立刻应用一个类似于:hover的伪类。为了解决这种以及其他情况,sass提供了一个特殊结构&

3-1. 父选择器的标识符&
一般情况下,sass在解开一个嵌套规则时就会把父选择器(#content)通过一个空格连接到子选择器的前边(articleaside)形成(#content article#content aside)。这种在CSS里边被称为后代选择器,因为它选择IDcontent的元素内所有命中选择器articleaside的元素。但在有些情况下你却不会希望sass使用这种后代选择器的方式生成这种连接。

最常见的一种情况是当你为链接之类的元素写:hover这种伪类时,你并不希望以后代选择器的方式连接。比如说,下面这种情况sass就无法正常工作:

1
2
3
4
5
6
7
8
article a {
color: green;
:hover { color: red }
}
/* 编译后 */
article a { color: blue; }
article a :hover { color: red; } /* 注意: a 与 :hover 之间有空格 */

原本,我们是希望把样式应用到 article 元素的子元素 a 上,也就是应用在超链接上。但是实际的结果是样式会被应用到article a匹配的元素的所有子元素,也就是超链接里面的所有子元素在被hover时都会变成红色。
cmd-markdown-logo
解决之道为使用一个特殊的sass选择器,即父选择器。在使用嵌套规则时,父选择器能对于嵌套规则如何解开提供更好的控制。它就是一个简单的&符号,且可以放在任何一个选择器可出现的地方。

1
2
3
4
5
6
7
8
article a {
color: blue;
&:hover { color: red }
}
/* 编译后 */
article a { color: blue }
article a:hover { color: red } /* 注意: a 与 :hover 之间没有空格 */

当包含父选择器标识符的嵌套规则被打开时,它不会像后代选择器那样进行拼接,而是&被父选择器直接替换。
cmd-markdown-logo
同时父选择器标识符还有另外一种用法,你可以在父选择器之前添加选择器。举例来说,当用户在使用IE浏览器时,你会通过JavaScriptbody标签上添加一个ie的类名,为这种情况编写特殊的样式如下:

1
2
3
4
5
6
7
8
#content aside {
color: red;
body.ie & { color: green }
}
/*编译后*/
#content aside {color: red};
body.ie #content aside { color: green }

3-2. 群组选择器的嵌套
CSS里边,.button button会命中button元素和类名为.button的元素。这种选择器称为群组选择器。群组选择器 的规则会对命中群组中任何一个选择器的元素生效。

1
.container h1, .container h2, .container h3 { margin-bottom: .8em }

sass解开一个群组选择器规则内嵌的规则时,它会把每一个内嵌选择器的规则都正确地解出来:

1
2
3
.container {
h1, h2, h3 {margin-bottom: .8em}
}

对于内嵌在群组选择器内的嵌 套规则,处理方式也一样:

1
2
3
4
5
6
nav, aside {
a {color: blue}
}
/*编译后*/
nav a, aside a {color: blue}

有利必有弊,你需要特别注意群组选择器的规则嵌套生成的css。虽然sass让你的样式表看上去很小,但实际生成的css却可能非常大,这会降低网站的速度。

3-3. 子组合选择器和同层组合选择器:>、+和~
你可以用同层相邻组合选择器+选择header元素后紧跟的p元素:

1
header + p { font-size: 1.1em }

你也可以用同层全体组合选择器~,选择所有跟在article后的同层p元素,不管它们之间隔了多少其他元素:

1
article ~ p { border-top: 1px dashed #ccc }

这些组合选择器可以毫不费力地应用到sass的规则嵌套中。可以把它们放在外层选择器后边,或里层选择器前边:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
article {
~ p { border-top: 1px dashed #ccc }
> footer { background: #eee }
dl > {
dt { color: #333 }
dd { color: #555 }
}
nav + & { margin-top: 0 }
}
/*编译后*/
article ~ p { border-top: 1px dashed #ccc }
article > footer { background: #eee }
article dl > dt { color: #333 }
article dl > dd { color: #555 }
nav + article { margin-top: 0 }

3-4. 嵌套属性
嵌套属性的规则是这样的:把属性名从中划线-的地方断开,在根属性后边添加一个冒号:,紧跟一个{ }块,把子属性部分写在这个{ }块中。就像css选择器嵌套一样,sass会把你的子属性一一解开,把根属性和子属性部分通过中划线-连接起来,最后生成的效果与你手动一遍遍写的css样式一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
nav {
border: {
style: solid;
width: 1px;
color: #ccc;
}
}
/*编译后*/
nav {
border-style: solid;
border-width: 1px;
border-color: #ccc;
}

有人就发现了,我用css可以直接写border的属性。

1
border { solid 1px #ccc }

这样不是更简单?其实对于属性的缩写形式,你还可以像下边这样来嵌套:

1
2
3
4
5
6
7
8
9
10
11
12
nav {
border: 1px solid #ccc {
left: 0px;
right: 0px;
}
}
/*编译后*/
nav {
border: 1px solid #ccc;
border-left: 0px;
border-right: 0px;
}

但是随着你的样式表变得越来越大,这种写法也很难保持结构清晰。有时,处理这种大量样式的唯一方法就是把它们分拆到多个文件中。
4. 导入SASS文件
当通过@importsass样式分散到多个文件时,你通常只想生成少数几个css文件。那些专门为@import命令而编写的sass文件,并不需要生成对应的独立css文件,这样的sass文件称为局部文件。对此,sass有一个特殊的约定来命名这些文件。

此约定即,sass局部文件的文件名以下划线开头。这样,sass就不会在编译时单独编译这个文件输出css,而只把这个文件用作导入。当你@import一个局部文件时,还可以不写文件的全名,即省略文件名开头的下划线。举例来说,你想导入themes/_night-sky.scss这个局部文件里的变量,你只需在样式表中写@import “themes/night-sky”;

4-1. 默认变量值
一般情况下,反复声明一个变量,只有最后一处声明有效且它会覆盖前边的值.

1
2
3
4
5
$a-color: blue;
$a-color: red;
a {
color: $a-color;
}

在上边这段代码,链接的颜色会被设置成red,使用sass!default标签可以让使用者定制修改sass库文件中的某些值。它很像css属性中!important标签的对立,不同的是!default用于变量,含义是:如果这个变量被声明赋值了,那就用它声明的值,否则就用这个默认值

1
2
3
4
$fancybox-width: 400px !default;
.fancybox {
width: $fancybox-width;
}

在上例中,如果用户在导入你的sass局部文件之前声明了一个$fancybox-width变量,那么你的局部文件中对$fancybox-width赋值400px的操作就无效。如果用户没有做这样的声明,则$fancybox-width将默认为400px
4-2. 嵌套导入
sass允许@import命令写在css规则内。这种导入方式下,生成对应的css文件时,局部文件会被直接插入到css规则内导入它的地方。这是一个名为_blue-theme.scss的局部文件:

1
2
3
4
aside {
background: blue;
color: white;
}

然后把它导入到一个CSS规则内,如下所示:
.blue-theme {@import “blue-theme”}

//生成的结果跟你直接在.blue-theme选择器内写_blue-theme.scss文件的内容完全一样。

1
2
3
4
5
6
.blue-theme {
aside {
background: blue;
color: #fff;
}
}

被导入的局部文件中定义的所有变量和混合器,也会在这个规则范围内生效。这些变量和混合器不会全局有效,这样我们就可以通过嵌套导入只对站点中某一特定区域运用某种颜色主题或其他通过变量配置的样式。
4-3. 原生的CSS导入
你不能用sass@import直接导入一个原始的css文件,因为sass会认为你想用css原生的@import。但是,因为sass的语法完全兼容css,所以你可以把原始的css文件改名为.scss后缀,即可直接导入了。

5. 静默注释
在原生的css中,注释对于其他人是直接可见的。sass另外提供了一种不同于css标准注释格式//的注释语法,即静默注释,其内容不会出现在生成的css文件中。

1
2
3
4
body {
color: #333; // 这种注释内容不会出现在生成的css文件中(静默注释)
padding: 0; /* 这种注释内容会出现在生成的css文件中 */
}

css的标准注释格式//内的注释内容亦可在生成的css文件中抹去。当注释出现在原生css不允许的地方,如在css属性或选择器中,sass将不知如何将其生成到对应css文件中的相应位置,于是这些注释被抹掉。

1
2
3
4
body {
color /* 这块注释内容不会出现在生成的css中 */: #333;
padding: 1; /* 这块注释内容也不会出现在生成的css中 */ 0;
}

6. 混合器
混合器使用@mixin标识符定义。看上去很像其他的CSS @标识符,比如说@media或者@font-face。这个标识符给一大段样式赋予一个名字,这样你就可以轻易地通过引用这个名字重用这段样式。下边的这段sass代码,定义了一个非常简单的混合器,目的是添加跨浏览器的圆角边框。

1
2
3
4
5
@mixin rounded-corners {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}

然后就可以在你的样式表中通过@include来使用这个混合器,放在你希望的任何地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
notice {
background-color: green;
border: 2px solid #00aa00;
@include rounded-corners;
}
//sass最终生成:
.notice {
background-color: green;
border: 2px solid #00aa00;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}

6-2. 混合器中的CSS规则
混合器中不仅可以包含属性,也可以包含css规则,包含选择器和选择器中的属性,当一个包含css规则的混合器通过@include包含在一个父规则中时,在混合器中的规则最终会生成父规则中的嵌套规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@mixin no-bullets {
list-style: none;
li {
list-style-image: none;
list-style-type: none;
margin-left: 0px;
}
}
ul.plain {
color: #444;
@include no-bullets;
}
/* 编译为 */
ul.plain {
color: #444;
list-style: none;
}
ul.plain li {
list-style-image: none;
list-style-type: none;
margin-left: 0px;
}

混合器中的规则甚至可以使用sass的父选择器标识符&。使用起来跟不用混合器时一样,sass解开嵌套规则时,用父规则中的选择器替代&。

6-3. 给混合器传参
给混合器传参数来让混合器变得更加灵活和可重用,混合器并不一定总得生成相同的样式。可以通过在@include混合器时给混合器传参,来定制混合器生成的精确样式。当@include混合器时,参数其实就是可以赋值给css属性值的变量。

1
2
3
4
5
@mixin link-colors($normal, $hover, $visited) {
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}

当混合器被@include时,你可以把它当作一个css函数来传参。

1
2
3
4
5
6
7
8
9
a {
@include link-colors(blue, red, green);
}
//Sass最终生成的是:
a { color: blue; }
a:hover { color: red; }
a:visited { color: green; }

sass允许通过语法$name: value的形式指定每个参数的值。这种形式的传参,参数顺序就不必再在乎了,只需要保证没有漏掉参数即可:

1
2
3
4
5
6
7
a {
@include link-colors(
$normal: blue,
$visited: green,
$hover: red
);
}

6-4. 默认参数值
@include混合器时不必传入所有的参数,我们可以给参数指定一个默认值。参数默认值使用$name: default-value的声明形式,默认值可以是任何有效的css属性值,甚至是其他参数的引用,如下代码:

1
2
3
4
5
6
7
8
9
10
@mixin link-colors(
$normal,
$hover: $normal,
$visited: $normal
)
{
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}

如果这样调用:@include link-colors(red)$hover$visited也会被自动赋值为red。

7. 使用选择器继承来精简CSS
选择器继承是说一个选择器可以继承为另一个选择器定义的所有样式。这个通过@extend语法实现,如下代码:

1
2
3
4
5
6
7
8
.error {
border: 1px red;
background-color: #fdd;
}
.seriousError {
@extend .error; //通过选择器继承继承.error样式
border-width: 3px;
}

在上边的代码中,.seriousError将会继承样式表中任何位置处为.error定义的所有样式,而且任何跟.error有关的组合选择器样式也会被.seriousError以组合选择器的形式继承,如下代码:

1
2
3
4
5
6
7
8
//.seriousError从.error继承样式
.error a{ //也应用到.seriousError a
color: red;
font-weight: 100;
}
h1.error { //同样会应用到hl.seriousError
font-size: 1.2rem;
}

7-1. 何时使用继承
当一个元素拥有的类(比如说.seriousError)表明它属于另一个类(比如说.error),也就是说classseriousErrorclasserror的子元素的时候,这时使用继承再合适不过了。

7-2. 继承的高级用法
下来的这段代码定义了一个名为disabled的类,样式修饰使它看上去像一个灰掉的超链接。通过继承a这一超链接元素来实现:

1
2
3
4
.disabled {
color: gray;
@extend a;
}

假如一条样式规则继承了一个复杂的选择器,那么它只会继承这个复杂选择器命中的元素所应用的样式。
关于@extend有两个要点:

  • 跟混合器相比,继承生成的css代码相对更少。因为继承仅仅是重复选择器,而不会重复属性,所以使用继承往往比混合器生成的css体积更小。如果你非常关心你站点的速度,请牢记这一点。
  • 继承遵从css层叠的规则。当两个不同的css规则应用到同一个html元素上时,并且这两个不同的css规则对同一属性的修饰存在不同的值,css层叠规则会决定应用哪个样式。相当直观:通常权重更高的选择器胜出,如果权重相同,定义在后边的规则胜出。

被继承的样式会保持原有定义位置和选择器权重不变。
7-3. 使用继承的最佳实践
不要在css规则中使用后代选择器(比如.foo .bar)去继承css规则。如果你这么做,同时被继承的css规则有通过后代选择器修饰的样式,生成css中的选择器的数量很快就会失控:

1
2
.foo .bar { @extend .baz; }
.bip .baz { a: b; }

在上边的例子中,sass必须保证应用到.baz的样式同时也要应用到.foo .bar(位于class=”foo”的元素内的class=”bar”的元素)。例子中有一条应用到.bip .baz(位于class=”bip”的元素内的class=”baz”的元素)的css规则。当这条规则应用到.foo .bar时,可能存在三种情况,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 继承可能迅速变复杂 -->
<!-- Case 1 -->
<div class="foo">
<div class="bip">
<div class="bar">...</div>
</div>
</div>
<!-- Case 2 -->
<div class="bip">
<div class="foo">
<div class="bar">...</div>
</div>
</div>
<!-- Case 3 -->
<div class="foo bip">
<div class="bar">...</div>
</div>

只要你想,你完全可以放心地继承有后代选择器修饰规则的选择器,不管后代选择器多长,但有一个前提就是,不要用后代选择器去继承

8. 小结

本文介绍了sass最基本部分,你可以轻松地使用sass编写清晰、无冗余、语义化的css,当然这还需要多家练习。
变量是sass提供的最基本的工具。通过变量可以让独立的css值变得可重用,无论是在一条单独的规则范围内还是在整个样式表中。变量、混合器的命名甚至sass的文件名,可以互换通用_和-。同样基础的是sass的嵌套机制。嵌套允许css规则内嵌套css规则,减少重复编写常用的选择器,同时让样式表的结构一眼望去更加清晰。sass同时提供了特殊的父选择器标识符&,通过它可以构造出更高效的嵌套。

还有sass*8的另一个重要特性,样式导入。通过样式导入可以把分散在多个sass文件中的内容合并生成到一个css文件,避免了项目中有大量的css文件通过原生的css @import带来的性能问题。通过嵌套导入和默认变量值,导入可以构建更强有力的、可定制的样式。混合器允许用户编写语义化样式的同时避免视觉层面上样式的重复。你不仅学到了如何使用混合器减少重复,同时学习到了如何使用混合器让你的css变得更加可维护和语义化。最后,我们学习了与混合器相辅相成的选择器继承。继承允许你声明类之间语义化的关系,通过这些关系可以保持你的css**的整洁和可维护性。